// 📦 引入工具库
const _ = require("lodash");
const path = require("path");
const fs = require("fs");

const esm = require("esm");
const requireModule = esm(module);

// 读取历史开奖数据的函数
const { readAllValues } = requireModule(
  "../../../awakened-potential/helpers/readAllValues.js"
);
// 检查预测准确性的函数
const checkAccuracy = requireModule("../../utils/checkAccuracy.js");

//  读取历史开奖数据
const allValues = readAllValues("m");

// 号码池（01 ~ 49）
const allNumbers = Array.from({ length: 49 }, (_, i) =>
  (i + 1).toString().padStart(2, "0")
);

// 是否使用窗口
const useWindow = true;
// 窗口大小
const windowSize = 109;

// 存储预测结果的数组
let predictResults = [];

// 记录正确的预测次数
let correct = 0;
// 记录最大连续错误次数
let maxWrongStreak = 0;
// 当前连续错误次数
let currentWrongStreak = 0;

// 计算周期：找出号码出现位置间隔
const calculateCycle = (numbers, currentNumber) => {
  const positions = []; // 存储当前号码出现的位置
  numbers.forEach((num, idx) => {
    if (num === currentNumber) {
      positions.push(idx); // 如果当前号码匹配，将其位置加入 positions 数组
    }
  });

  const cycles = []; // 存储周期（位置间隔）
  for (let i = 1; i < positions.length; i++) {
    cycles.push(positions[i] - positions[i - 1]); // 计算位置之间的间隔，作为周期
  }
  return cycles; // 返回周期数组
};

// ✅ 周期性分析策略：从所有号码中筛选周期短的
const cycleAnalysisStrategy = (temp) => {
  const previousNumbers = temp.map((v) => v.number); // 获取当前周期的号码列表
  const cycleResults = []; // 存储周期分析结果

  const cycleThreshold = 5; // 设置周期阈值，周期小于此值的号码将被选中
  allNumbers.forEach((num) => {
    const cycle = calculateCycle(previousNumbers, num); // 计算当前号码的周期
    if (cycle.length > 0 && cycle.length <= cycleThreshold) {
      cycleResults.push(num); // 如果周期长度小于等于阈值，加入结果
    }
  });

  return cycleResults; // 返回符合条件的周期性号码
};

// ✅ 选择32个不重复号码，如果不足则补充
const select32Numbers = (cycleResults) => {
  const unique = new Set(); // 使用 Set 来保证号码唯一

  for (let num of cycleResults) {
    if (typeof num === "number") num = num.toString().padStart(2, "0"); // 转换为两位数字符串
    if (typeof num === "string") num = num.padStart(2, "0"); // 确保是两位数格式
    if (/^\d{2}$/.test(num)) unique.add(num); // 只添加符合正则的号码
  }

  let result = Array.from(unique); // 将 Set 转换为数组

  // 🚫 如果超过 32 个，就裁剪
  if (result.length > 32) {
    result = result.slice(0, 32); // 如果超过 32 个，取前 32 个
  }

  // 🚨 如果不足 32 个，就补充
  if (result.length < 32) {
    const already = new Set(result); // 已选号码集合
    const candidates = allNumbers.filter((n) => !already.has(n)); // 找出未选号码
    for (let i = 0; i < candidates.length && result.length < 32; i++) {
      result.push(candidates[i]); // 补充未选号码直到 32 个
    }
  }

  // ✅ 最终校验
  if (result.length !== 32) {
    throw new Error(
      `❗ 无法生成 32 个不重复号码（目前只有 ${result.length} 个）`
    ); // 如果结果不符合要求，抛出异常
  }

  return result; // 返回最终选择的 32 个号码
};

// ✅ 主循环：遍历每一期数据
allValues.forEach((item, index) => {
  const temp = useWindow
    ? allValues.slice(index - windowSize, index) // 使用窗口策略，取出窗口内的数据
    : allValues.slice(0, index); // 不使用窗口策略，取出从头到当前期的数据

  if (temp.length === 0) return; // 如果 temp 为空，跳过本期

  const cycleAnalysisResult = cycleAnalysisStrategy(temp); // 进行周期性分析
  // console.log(`周期性分析结果: ${cycleAnalysisResult.join(",")}`);

  const selectedNumbers = select32Numbers(cycleAnalysisResult); // 从周期分析结果中选择 32 个号码
  // console.log(`选出的32个号码: ${selectedNumbers.join(",")}`);

  if (selectedNumbers.length !== 32) {
    throw new Error("选出的32个号码数量不正确"); // 如果选出的号码数不等于 32，抛出异常
  }

  try {
    const isPredictionCorrect = checkAccuracy(selectedNumbers, item.number); // 检查预测是否正确
    if (isPredictionCorrect) {
      correct += 1; // 如果预测正确，增加正确次数
      currentWrongStreak = 0; // 重置连续错误计数
    } else {
      currentWrongStreak += 1; // 如果预测错误，增加当前错误计数
      maxWrongStreak = Math.max(maxWrongStreak, currentWrongStreak); // 更新最大连续错误次数
    }
    predictResults.push(selectedNumbers); // 将当前期的预测结果加入结果集
  } catch (error) {
    console.error(`Error in checkAccuracy at index ${index}: ${error.message}`); // 捕获并打印错误
  }
});

// ✅ 输出统计
const total = predictResults.length; // 计算预测的总期数
const accuracy = total > 0 ? ((correct / total) * 100).toFixed(2) : 0; // 计算准确率

console.log(`预测总数: ${total}`); // 输出预测总数
console.log(`命中次数: ${correct}`); // 输出命中次数
console.log(`准确率: ${accuracy}%`); // 输出准确率
console.log(`最大连续错误次数: ${maxWrongStreak}`); // 输出最大连续错误次数
